using System;
using System.Collections;
using Slaks.VectorBasics.Containers;

namespace Slaks.VectorBasics.Analysis
{
	public class ClusteringAnalysis
	{
		public ClusteringAnalysis()
		{
		}

		public static double GetRandIndex(VectorList[] clusters, Document2CategoryMap document2categoryMap)
		{
			double randIndex = 0.0;

			long agreement = 0;
			long disagreement = 0;

			Vector vector1, vector2;
			string category1, category2;

			//AddCentroidsToList(clusters);

			//iterate through clusters
			for (int i = 0; i < clusters.Length; i++)
			{
				//start with the first cluster (i) and increase
				for (int j = 0; j < clusters[i].Count; j++)
				{
					vector1 = clusters[i][j]; //take vector from cluster i at position j

					for (int m = i; m < clusters.Length; m++)
					{
						//if the i cluster equals m cluster then start from the next vector, otherwise start from the first vector
						for (int k = ((m == i) ? (j + 1) : 0); k < clusters[m].Count; k++)
						{
							vector2 = clusters[m][k]; //take vector from cluster m at position k

							category1 = ((document2categoryMap == null) ? vector1.Category : document2categoryMap[vector1.Name]);
							category2 = ((document2categoryMap == null) ? vector2.Category : document2categoryMap[vector2.Name]);

							if (category1 == category2 && i == m)
							{
								++agreement;
							}
							else if (category1 != category2 && i != m)
							{
								++agreement;
							}
							else ++disagreement;
						}
					}
				}
			}
			randIndex = (double) agreement/(agreement + disagreement);

			//RemoveCentroidsFromList(clusters);

			return randIndex;
		}

		public static double GetMutualInformation(VectorList[] clusters, Document2CategoryMap document2categoryMap, IList categoryList, int numberOfVectors)
		{
			double mutualInformation = 0.0;
			int numberOfAlgorithmClusters = clusters.Length;
			int numberOfGroundTruthClusters = categoryList.Count;
			int logBase = numberOfAlgorithmClusters*numberOfGroundTruthClusters;

			double tempPart;

			int numberOfItems;

			int innerFirst = 0, innerSecond = 0;

			//AddCentroidsToList(clusters);

			for (int l = 0; l < numberOfAlgorithmClusters; l++)
			{
				for (int h = 0; h < numberOfGroundTruthClusters; h++)
				{
					numberOfItems = GetNumberOfClustersIJ(clusters, categoryList, document2categoryMap, l, h);

					tempPart = numberOfItems*numberOfVectors;

					innerFirst = innerSecond = 0;
					for (int i = 0; i < numberOfAlgorithmClusters; i++)
					{
						innerFirst += GetNumberOfClustersIJ(clusters, categoryList, document2categoryMap, i, h);
					}

					for (int i = 0; i < numberOfGroundTruthClusters; i++)
					{
						innerSecond += GetNumberOfClustersIJ(clusters, categoryList, document2categoryMap, l, i);
					}

					if (tempPart != 0.0) tempPart /= (innerFirst*innerSecond);
					if (tempPart != 0.0) tempPart = Math.Log(tempPart, logBase);

					mutualInformation += numberOfItems*tempPart;
				}
			}

			if (mutualInformation != 0.0) mutualInformation /= numberOfVectors;

			//RemoveCentroidsFromList(clusters);
			return mutualInformation;
		}

		private static int GetNumberOfClustersIJ(VectorList[] clusters, IList categoryList, Document2CategoryMap document2categoryMap, int indexI, int indexJ)
		{
			int number = 0;

			string category = (string) categoryList[indexJ];
			string vectorCategory;

			for (int i = 0; i < clusters[indexI].Count; i++)
			{
				vectorCategory = (document2categoryMap == null) ? clusters[indexI][i].Category : (document2categoryMap[clusters[indexI][i].Name]);
				if (string.Equals(category, vectorCategory)) ++number;
			}
			return number;
		}

		public static double GetDunnIndex(VectorList[] clusters, VectorDistanceMeasure vectorDistanceMeasure)
		{
			double dMin = double.MaxValue;
			double dMax = double.MinValue;

			double distance;

			Vector vector1, vector2;

			//compute maximum distance between any two items in the same cluster
			for (int i = 0; i < clusters.Length; i++)
			{
				for (int j = 0; j < clusters[i].Count; j++)
				{
					vector1 = clusters[i][j];
					for (int k = j + 1; k < clusters[i].Count; k++)
					{
						vector2 = clusters[i][k];
						distance = vectorDistanceMeasure(vector1, vector2);
						if (dMax < distance) dMax = distance;
					}
				}
			}

			//compute minimum distance between any two items in different clusters
			for (int i = 0; i < clusters.Length - 1; i++)
			{
				for (int j = 0; j < clusters[i].Count; j++)
				{
					vector1 = clusters[i][j];
					for (int k = 0; k < clusters.Length; k++)
					{
						if (k == i) continue; //clusters must be different

						for (int m = 0; m < clusters[k].Count; m++)
						{
							vector2 = clusters[k][m];
							distance = vectorDistanceMeasure(vector1, vector2);
							if (dMin > distance) dMin = distance;
						}
					}
				}
			}

			double dunnIndex = (dMin != 0) ? (dMin/dMax) : dMin;

			return dunnIndex;
		}


	}
}